﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Objects = BomberManServer.Gaming.Object;
using BomberManServer.Gaming.Object.Stats;
using System.Threading;
using BomberManServer.Utils;
using System.ServiceModel;
using BomberManServer.Gaming;

namespace BomberManServer.WebService
{
    [CallbackBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant)]
    class ProxyService : IDisposable, IServiceGameServerCallback
    {
        // the contract with the WS
        // the object to communicate with the main thread
        ConcurrentDoubleBufferedLinkedList<Objects.Match> matchs;
        ConcurrentDoubleBufferedLinkedList<GameStats> stats;
        ConcurrentDoubleBufferedLinkedList<GameCreatedArgs> games;
        Thread thread;
        ServiceGameServerClient client;
        Random tokenizer;
        public bool connected { get; private set; }

        public ProxyService()
        {
            matchs = new ConcurrentDoubleBufferedLinkedList<Objects.Match>();
            stats = new ConcurrentDoubleBufferedLinkedList<GameStats>();
            games = new ConcurrentDoubleBufferedLinkedList<GameCreatedArgs>();
            tokenizer = new Random();
            connected = false;
        }

        public void Dispose()
        {
            client.Close();
        }

        public void start()
        {
            thread = new Thread(run);
            thread.Start();
        }

        // thread entry point
        private void run()
        {
            try
            {
                // register in the WS
                InstanceContext context = new InstanceContext(this);
                client = new ServiceGameServerClient(context, "service");
                client.register();
                connected = true;

                GameStats stat;
                GameCreatedArgs created;
                while (true)
                {
                    // look if we have a message from the core
                    // look if a game has finished
                    if (stats.consumer.consume(out stat))
                    {
                        // send to the webservice
                        client.onGameFinished(format(stat));
                        continue;
                    }
                    // look if a game has sarted
                    if (games.consumer.consume(out created))
                    {
                        // send to the webservice
                        client.onGameCreated(created);
                        continue;
                    }
                    Thread.Sleep(0);
                }
            }
            catch (EndpointNotFoundException e)
            {
            }
        }

        // Functions for the main loop
        public void onGameFinished(GameStats stat)
        {
            stats.producer.produce(stat);
        }

        public void onGameStarted(int port,Objects.Match match)
        {
                GameCreatedArgs g = new GameCreatedArgs();
                g.game_id = match.uid;
                g.port = (uint) port;
                g.players = new GameCreatedArgs_Player[match.players.Count];
                int index = 0;
                foreach (Objects.Player p in match.players)
                {
                    GameCreatedArgs_Player player = new GameCreatedArgs_Player();
                    player.uid = p.uid;
                    player.token = p.token;
                    g.players[index] = player;
                    index++;
                }
                games.producer.produce(g);
        }

        public bool checkMatchRequest(out Objects.Match match)
        {
            return (matchs.consumer.consume(out match));
        }

        // Callback functions
        void IServiceGameServerCallback.newGame(Match m)
        {
            Objects.Match match = new Objects.Match(m.game_id, (Objects.Match.MatchType) m.type);
            // fill the match
            foreach(Team t in m.teams) {
                Objects.Team team = new Objects.Team(t.id);
                foreach(Player p in t.players) {
                        // generate a token
                        UInt16 token = (UInt16) tokenizer.Next(UInt16.MaxValue);
                        team.add(new Objects.Player(token,p.uid,p.nickname,p.speed,p.delay,p.bomb,p.range));
                    }
                match.add(team);
             }

            // we swap the match to the main thread
            matchs.producer.produce(match);
        }

        // Private functions
        private GameFinishedArgs format(GameStats stat)
        {
            GameFinishedArgs g = new GameFinishedArgs();
            g.game_id = stat.game_id;
            g.winner_id = stat.getWinnerID();
            g.teams = new GameFinishedArgs_Team[stat.teamStats.Length];
            
            uint team_index = 0;
            foreach ( TeamStats t in stat.teamStats)
            {
                GameFinishedArgs_Team team = new GameFinishedArgs_Team();
                // TODO get team score
                //team.score = 
                team.team_id = t.teamID;
                g.teams[team_index] = team;

                uint player_index = 0;
                foreach (PlayerStats p in t.players)
                {
                    GameFinishedArgs_Player player = new GameFinishedArgs_Player();
                    player.deaths = p.nbDeath;
                    player.kills = p.nbKill;
                    player.suicides = p.nbSuicide;
                    player_index++;
                }

                team_index++;
            }
            
            return g;
        }
    }
}
